package com.bbva.kyfb.servicios.host;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.bbva.jee.arq.spring.core.auditoria.AdministradorEventos;
import com.bbva.jee.arq.spring.core.constantes.ConstantesContexto;
import com.bbva.jee.arq.spring.core.constantes.ConstantesLog;
import com.bbva.jee.arq.spring.core.contexto.ArqSpringContext;
import com.bbva.jee.arq.spring.core.host.ExcepcionConfiguracion;
import com.bbva.jee.arq.spring.core.host.ExcepcionTransaccion;
import com.bbva.jee.arq.spring.core.host.RegistroTransacciones;
import com.bbva.jee.arq.spring.core.host.ServicioTransacciones;
import com.bbva.jee.arq.spring.core.host.TipoProtocolo;
import com.bbva.jee.arq.spring.core.host.TipoTransporte;
import com.bbva.jee.arq.spring.core.host.descriptor.DescriptorConfiguracionProtocolo;
import com.bbva.jee.arq.spring.core.host.descriptor.DescriptorConfiguracionTransporte;
import com.bbva.jee.arq.spring.core.host.descriptor.DescriptorTransaccion;
import com.bbva.jee.arq.spring.core.host.descriptor.FactoriaDescriptoresTransaccion;
import com.bbva.jee.arq.spring.core.host.mock.RegistroMensajesMock;
import com.bbva.jee.arq.spring.core.host.protocolo.ManejadorProtocolo;
import com.bbva.jee.arq.spring.core.host.protocolo.elara.ManejadorProtocoloElara;
import com.bbva.jee.arq.spring.core.host.protocolo.otma.ManejadorProtocoloOtma;
import com.bbva.jee.arq.spring.core.host.protocolo.otma.pg.ManejadorProtocoloOtmaPg;
import com.bbva.jee.arq.spring.core.host.protocolo.ps9.ManejadorProtocoloPS9;
import com.bbva.jee.arq.spring.core.host.transporte.Transporte;
import com.bbva.jee.arq.spring.core.host.transporte.mq.TransporteMQ;
import com.bbva.jee.arq.spring.core.host.transporte.ws.TransporteWS;
import com.bbva.jee.arq.spring.core.log.I18nLog;
import com.bbva.jee.arq.spring.core.log.I18nLogFactory;
import com.bbva.jee.arq.spring.core.util.excepciones.PropiedadNoEncontradaExcepcion;

@Component
public class ServicioTransaccionesKYFB extends ServicioTransacciones {

	/** The Constant LOG. */
	private static final I18nLog LOG = I18nLogFactory.getLogI18n(ServicioTransaccionesKYFB.class, ConstantesLog.MENSAJES_HOST);


	/** The Constant PREFIJO_PROPIEDAD_CONFIGURACION. */
	public static final String PREFIJO_PROPIEDAD_CONFIGURACION = "servicioTransacciones.configuracion.";

	/** The Constant PREFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO. */
	public static final String PREFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO = PREFIJO_PROPIEDAD_CONFIGURACION + "protocolo.";

	/** The Constant PREFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO_SESION. */
	public static final String PREFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO_SESION = PREFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO + "sesion";

	/** The Constant PREFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE. */
	public static final String PREFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE = PREFIJO_PROPIEDAD_CONFIGURACION + "transporte.";

	/** The Constant PREFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE_SESION. */
	public static final String PREFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE_SESION = PREFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE + "sesion";

	/** The Constant SUFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO_TIPO. */
	private static final String SUFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO_TIPO = ".tipo";

	/** The Constant SUFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE_TIPO. */
	private static final String SUFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE_TIPO = ".tipo";

	/** The Constant SUFIJO_PROPIEDAD_CONFIGURACION_GRABACION. */
	public static final String SUFIJO_PROPIEDAD_CONFIGURACION_GRABACION = "grabacion";

	/** The Constant SUFIJO_PROPIEDAD_CONFIGURACION_DIRECTORIO_GRABACION. */
	public static final String SUFIJO_PROPIEDAD_CONFIGURACION_DIRECTORIO_GRABACION = SUFIJO_PROPIEDAD_CONFIGURACION_GRABACION + ".directorio";

	/** The protocolos. */
	private final Map<TipoProtocolo,ManejadorProtocolo> protocolos = new HashMap<TipoProtocolo,ManejadorProtocolo>();

	/** The transportes. */
	private final Map<TipoTransporte,Transporte> transportes = new HashMap<TipoTransporte,Transporte>();

	/** The registro mensajes mock. */
	private RegistroMensajesMock registroMensajesMock;

	/** The spring context. */
	@Resource
	protected ArqSpringContext springContext;

	/** The administrador eventos. */
	@Autowired
	private AdministradorEventos administradorEventos;

	@Autowired
	private RegistroTransacciones registroTransacciones;

	/**
	 * Inicializar.
	 *
	 * @throws Exception the exception
	 */
	@Override
	@PostConstruct
	public void inicializar() throws Exception {

		// protocolo OTMA:
		final ManejadorProtocoloOtma manejadorProtocoloOtma = new ManejadorProtocoloOtmaKYFB();
		manejadorProtocoloOtma.setAdministradorEventos(administradorEventos);
		manejadorProtocoloOtma.setRegistroTransacciones(registroTransacciones);
		protocolos.put(TipoProtocolo.OTMA, manejadorProtocoloOtma);

		// protocolo OTMA-PG:
		final ManejadorProtocoloOtmaPg manejadorProtocoloOtmaPg = new ManejadorProtocoloOtmaPg();
		manejadorProtocoloOtmaPg.setAdministradorEventos(administradorEventos);
		manejadorProtocoloOtmaPg.setRegistroTransacciones(registroTransacciones);
		protocolos.put(TipoProtocolo.OTMA_PG, manejadorProtocoloOtmaPg);

		// protocolo ELARA:
		final ManejadorProtocoloElara manejadorProtocoloElara = new ManejadorProtocoloElara();
		manejadorProtocoloElara.setAdministradorEventos(administradorEventos);
		manejadorProtocoloElara.setRegistroTransacciones(registroTransacciones);
		protocolos.put(TipoProtocolo.ELARA, manejadorProtocoloElara);

		// procolo PS9:
		final ManejadorProtocoloPS9 manejadorProtocoloPS9 = new ManejadorProtocoloPS9();
		manejadorProtocoloPS9.setAdministradorEventos(administradorEventos);
		manejadorProtocoloPS9.setRegistroTransacciones(registroTransacciones);
		protocolos.put(TipoProtocolo.PS9, manejadorProtocoloPS9);

		// transporte MQ:
		try {
			final Transporte transporteMQ = new TransporteMQ();
			// si no hacemos esto, los fallos de configuracion los vamos a ver solo en tiempo
			// de ejecucion, y es mejor detectarlos en tiempo de despliegue
			transporteMQ.getFactoriaConexiones();
			transportes.put(TipoTransporte.MQ, transporteMQ);
		} catch ( final ExcepcionConfiguracion e ) {
			LOG.debug(e, "ServicioTransacciones.inicializar.errorTransporte", TipoTransporte.MQ);
		}

		// transporte WS:
		try {
			final Transporte transporteWS = new TransporteWS();
			// si no hacemos esto, los fallos de configuracion los vamos a ver solo en tiempo
			// de ejecucion, y es mejor detectarlos en tiempo de despliegue
			transporteWS.getFactoriaConexiones();
			transportes.put(TipoTransporte.WS, transporteWS);
		} catch ( final ExcepcionConfiguracion e ) {
			LOG.debug(e, "ServicioTransacciones.inicializar.errorTransporte", TipoTransporte.WS);
		}

		// registro de mensajes mock
		if ( isGrabacion() || isMock() ) {
			registroMensajesMock = new RegistroMensajesMock(getDirectorioGrabacion());
		}
	}

	/**
	 * Gets the directorio grabacion.
	 *
	 * @return the directorio grabacion
	 * @throws ExcepcionConfiguracion the excepcion configuracion
	 */
	private File getDirectorioGrabacion() throws ExcepcionConfiguracion {
		final String nombrePropiedad = PREFIJO_PROPIEDAD_CONFIGURACION + SUFIJO_PROPIEDAD_CONFIGURACION_DIRECTORIO_GRABACION;
		File dirGrabacion;
		try {
			final String nombreDirectorio = ArqSpringContext.getPropiedad(nombrePropiedad);
			dirGrabacion = new File(nombreDirectorio);
			if ( dirGrabacion.exists() ) {
				if (!dirGrabacion.isDirectory() || !dirGrabacion.canWrite()) {
					dirGrabacion = null;
				}
			} else if ( !dirGrabacion.mkdirs() ) {
				// convertimos a warning y devolvemos null
				//throw new ExcepcionConfiguracion(log.getMensaje("General.configuracion.valorPropiedadInvalido", nombrePropiedad, nombreDirectorio));
				LOG.warn("General.configuracion.valorPropiedadInvalido", nombrePropiedad, nombreDirectorio);
				dirGrabacion = null;
			}
		} catch ( final PropiedadNoEncontradaExcepcion e ) {
			// convertimos a warning y devolvemos null
			//throw new ExcepcionConfiguracion(log.getMensaje("General.configuracion.propiedadNoEncontrada", nombrePropiedad));
			LOG.debug("General.configuracion.propiedadNoEncontrada", nombrePropiedad);
			dirGrabacion = null;
		}
		return dirGrabacion;
	}

	/**
	 * Checks if is grabacion.
	 *
	 * @return true, if is grabacion
	 * @throws ExcepcionConfiguracion the excepcion configuracion
	 */
	private boolean isGrabacion() throws ExcepcionConfiguracion {
		boolean isGrab;
		final String propBool = PREFIJO_PROPIEDAD_CONFIGURACION + SUFIJO_PROPIEDAD_CONFIGURACION_GRABACION;
		try {
			isGrab = ArqSpringContext.getPropiedadBoolean(propBool);
		} catch ( final Exception e ) {
			LOG.debug("General.configuracion.propiedadNoEncontrada", propBool);
			isGrab = false;
		}
		return isGrab;
	}

	/**
	 * Checks if is mock.
	 *
	 * @return true, if is mock
	 * @throws ExcepcionConfiguracion the excepcion configuracion
	 */
	private boolean isMock() throws ExcepcionConfiguracion {
		boolean isMockBol;
		try {
			isMockBol = ArqSpringContext.getPropiedadBoolean(ConstantesContexto.TRANSACCIONES_MOCK);
		} catch ( final Exception e ) {
			LOG.debug("General.configuracion.propiedadNoEncontrada", ConstantesContexto.TRANSACCIONES_MOCK);
			isMockBol = false;
		}
		return isMockBol;
	}

	@Override
	public <P,R> R invocar(final Class<P> clasePeticion, final Class<R> claseRespuesta, final P transaccion) throws ExcepcionTransaccion {

		if ( clasePeticion == null ) {
			throw new ExcepcionTransaccion(LOG.getMensaje("General.configuracion.argumentoNulo", "clasePeticion"));
		}
		if ( claseRespuesta == null ) {
			throw new ExcepcionTransaccion(LOG.getMensaje("General.configuracion.argumentoNulo", "claseRespuesta"));
		}
		if ( transaccion == null ) {
			throw new ExcepcionTransaccion(LOG.getMensaje("General.configuracion.argumentoNulo", "transaccion"));
		}

		LOG.debug("ServicioTransacciones.invocar.transaccion", clasePeticion.getName());

		LOG.debug("ServicioTransacciones.invocar.descriptor");

		final DescriptorTransaccion<P,R> descriptorTransaccion = FactoriaDescriptoresTransaccion.getDescriptorTransaccion(clasePeticion, claseRespuesta);

		// obtenemos la configuracion del protocolo desde propiedades, y si no, desde anotaciones
		// guardando la definitiva en el descriptor de la transaccion
		final String configuracionProtocolo = descriptorTransaccion.getNombreConfiguracion();
		DescriptorConfiguracionProtocolo descriptorConfiguracionProtocolo;
		ExcepcionConfiguracion excepcionConfiguracionPropiedades = null;
		try {
			descriptorConfiguracionProtocolo = obtenerDescriptorConfiguracionProtocoloDesdePropiedades(configuracionProtocolo);
			descriptorTransaccion.setDescriptorConfiguracionProtocolo(descriptorConfiguracionProtocolo);
		} catch ( final ExcepcionConfiguracion e ) {
			LOG.debug("ServicioTransacciones.invocar.errorDescriptorConfiguracionProtocolo");
			excepcionConfiguracionPropiedades = e;
			descriptorConfiguracionProtocolo = descriptorTransaccion.getDescriptorConfiguracionProtocolo();
		}
		if ( descriptorConfiguracionProtocolo == null ) {
			if ( excepcionConfiguracionPropiedades == null ) {
				throw new ExcepcionConfiguracion(LOG.getMensaje("ServicioTransacciones.configuracion.noDisponible", configuracionProtocolo));
			}
			throw excepcionConfiguracionPropiedades;
		}

		// obtenemos la configuracion de transporte desde propiedades, y si no, desde anotaciones
		// guardando la definitiva en el descriptor de la transaccion
		final String configuracionTransporte = descriptorConfiguracionProtocolo.getNombreConfiguracionTransporte();
		DescriptorConfiguracionTransporte descriptorTransporte;
		excepcionConfiguracionPropiedades = null;
		try {
			descriptorTransporte = obtenerDescriptorConfiguracionTransporteDesdePropiedades(configuracionTransporte);
			descriptorTransaccion.setDescriptorConfiguracionTransporte(descriptorTransporte);
		} catch ( final ExcepcionConfiguracion e ) {
			LOG.debug("ServicioTransacciones.invocar.errorDescriptorTransporte");
			excepcionConfiguracionPropiedades = e;
			descriptorTransporte = descriptorTransaccion.getDescriptorConfiguracionTransporte();
		}
		if ( descriptorTransporte == null ) {
			if ( excepcionConfiguracionPropiedades == null ) {
				throw new ExcepcionConfiguracion(LOG.getMensaje("ServicioTransacciones.configuracion.noDisponible", configuracionTransporte));
			}
			throw excepcionConfiguracionPropiedades;
		}

		LOG.debug("ServicioTransacciones.invocar.descriptorObtenido", descriptorTransaccion);

		final R respuesta;

		if ( isMock() ) {
			LOG.info("ServicioTransacciones.invocar.mock");
			respuesta = registroMensajesMock.get(descriptorTransaccion, transaccion);
		} else {
			LOG.debug("ServicioTransacciones.invocar.manejadorProtocolo", descriptorConfiguracionProtocolo.getProtocolo());
			final ManejadorProtocolo manejadorProtocolo = protocolos.get(descriptorConfiguracionProtocolo.getProtocolo());
			if ( manejadorProtocolo == null ) {
				throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.recursoNoDisponible", descriptorConfiguracionProtocolo.getProtocolo()));
			}

			LOG.debug("ServicioTransacciones.invocar.manejadorTransporte", descriptorTransporte.getTipoTransporte());
			final Transporte transporte = transportes.get(descriptorTransporte.getTipoTransporte());
			if ( transporte == null ) {
				throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.recursoNoDisponible", descriptorTransporte.getTipoTransporte()));
			}

			LOG.debug("ServicioTransacciones.invocar.delegar.protocolo");
			respuesta = manejadorProtocolo.invocar(descriptorTransaccion, transporte, transaccion);
			if ( isGrabacion() ) {
				registroMensajesMock.put(descriptorTransaccion, transaccion, respuesta);
			}
		}
		return respuesta;
	}

	/**
	 * Obtener descriptor configuracion protocolo desde propiedades.
	 *
	 * @param nombreConfiguracion the nombre configuracion
	 * @return the descriptor configuracion protocolo
	 * @throws ExcepcionConfiguracion the excepcion configuracion
	 */
	private DescriptorConfiguracionProtocolo obtenerDescriptorConfiguracionProtocoloDesdePropiedades(final String nombreConfiguracion) throws ExcepcionConfiguracion {
		final String nombrePropiedad = PREFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO + nombreConfiguracion + SUFIJO_PROPIEDAD_CONFIGURACION_PROTOCOLO_TIPO;
		String tipoProtocoloString;
		try {
			tipoProtocoloString = ArqSpringContext.getPropiedad(nombrePropiedad);
		} catch ( final Exception e ) {
			LOG.error("General.configuracion.propiedadNoEncontrada.error", nombrePropiedad, e);
			throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.propiedadNoEncontrada", nombrePropiedad),e);
		}
		TipoProtocolo tipoProtocolo;
		try {
			tipoProtocolo = TipoProtocolo.valueOf(tipoProtocoloString.trim());
		} catch ( final IllegalArgumentException e ) {
			LOG.error("General.configuracion.valorPropiedadInvalido.error", nombrePropiedad, tipoProtocoloString, e);
			throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.valorPropiedadInvalido", nombrePropiedad, tipoProtocoloString),e);
		}
		final ManejadorProtocolo manejadorProtocolo = protocolos.get(tipoProtocolo);
		if ( manejadorProtocolo == null ) {
			throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.recursoNoDisponible", tipoProtocolo));
		}
		return manejadorProtocolo.getDescriptorConfiguracionProtocoloDesdePropiedades(nombreConfiguracion);
	}

	private DescriptorConfiguracionTransporte obtenerDescriptorConfiguracionTransporteDesdePropiedades(final String nombreConfiguracion) throws ExcepcionConfiguracion {
		final String nombrePropiedad = PREFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE + nombreConfiguracion + SUFIJO_PROPIEDAD_CONFIGURACION_TRANSPORTE_TIPO;
		String tipoTransporteString;
		try {
			tipoTransporteString = ArqSpringContext.getPropiedad(nombrePropiedad);
		} catch ( final Exception e ) {
			LOG.error("General.configuracion.propiedadNoEncontrada.error", nombrePropiedad, e);
			throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.propiedadNoEncontrada", nombrePropiedad),e);
		}
		TipoTransporte tipoTransporte;
		try {
			tipoTransporte = TipoTransporte.valueOf(tipoTransporteString.trim());
		} catch ( final IllegalArgumentException e ) {
			LOG.error("General.configuracion.valorPropiedadInvalido.error", nombrePropiedad, tipoTransporteString, e);
			throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.valorPropiedadInvalido", nombrePropiedad, tipoTransporteString),e);
		}
		final Transporte transporte = transportes.get(tipoTransporte);
		if ( transporte == null ) {
			throw new ExcepcionConfiguracion(LOG.getMensaje("General.configuracion.recursoNoDisponible", tipoTransporte));
		}
		return transporte.getDescriptorConfiguracionTransporteDesdePropiedades(nombreConfiguracion);
	}
}
